package com.example.emos.wx.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.example.emos.wx.config.SystemConstants;
import com.example.emos.wx.db.dao.*;
import com.example.emos.wx.db.pojo.TbCheckin;
import com.example.emos.wx.db.pojo.TbFaceModel;
import com.example.emos.wx.exception.EmosException;
import com.example.emos.wx.service.CheckinService;
import com.example.emos.wx.task.EmailTask;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

/**
 * @Author yctc
 * @Date 2021/3/13 15:26
 * @Version 1.0
 */
@Service
@Scope("prototype") //把当前java对象变成多例对象 -> 发送消息可以异步执行
@Slf4j
public class CheckinServiceImpl implements CheckinService {
    @Autowired
    private SystemConstants systemConstants;

    @Autowired
    private TbHolidaysDao holidaysDao;

    @Autowired
    private TbWorkdayDao workdayDao;

    @Autowired
    private TbCheckinDao checkinDao;

    @Autowired
    private TbFaceModelDao faceModelDao;

    @Autowired
    private TbCityDao cityDao;

    @Autowired
    private TbUserDao userDao;

    @Value("${emos.face.checkinUrl}")
    private String checkinUrl;

    @Value("${emos.face.createFaceModelUrl}")
    private String createFaceModelUrl;

    @Value("${emos.email.hr}")
    private String hrEmail;

    @Value("${emos.code}")
    private String code;

    @Autowired
    private EmailTask emailTask;

    /**
     * 判断是否具有签到功能
     */
    @Override
    public String validCanCheckIn(int userId, String date) {
        boolean bool_1 = holidaysDao.searchTodayIsHolidays() != null ? true : false; //查询当天是不是特殊节假日或者特殊工作日
        boolean bool_2 = workdayDao.searchTodayIsWorkday() != null ? true : false;   //返回结果不为空=true 反之
        String type = "工作日";
        if (DateUtil.date().isWeekend()) { // 判断指定日期是否是周末
            type = "节假日";
        }
        if (bool_1) {
            type = "节假日";
        } else if (bool_2) {
            type = "工作日";
        }

        if (type.equals("节假日")) {
            return "节假日不需要考勤";
        } else {
            DateTime now = DateUtil.date();  // 获取当前日期对象
            String start = DateUtil.today() + " " + systemConstants.attendanceStartTime;
            String end = DateUtil.today() + " " + systemConstants.attendanceEndTime;  // 考勤开始和结束时间封装成 字符串
            DateTime attendanceStart = DateUtil.parse(start);
            DateTime attendanceEnd = DateUtil.parse(end); //DateTime只能和DateTime比较  start和end 转换成日期对象
            if (now.isBefore(attendanceStart)) {
                return "没有到上班考勤开始时间";
            } else if (now.isAfter(attendanceEnd)) {
                return "超过了上班考勤结束时间";
            } else { //工作日
                HashMap map = new HashMap();
                map.put("userId", userId);
                map.put("date", date);
                map.put("start", start);
                map.put("end", end);
                boolean bool = checkinDao.haveCheckin(map) != null ? true : false;
                return bool ? "今日已经考勤，不用重复考勤" : "可以考勤";
            }
        }
    }

    /**
     * 实现签到方法
     */
    @Override
    public void checkIn(HashMap param) {
        //判断签到
        Date d1 = DateUtil.date();  //当前时间
        Date d2 = DateUtil.parse(DateUtil.today() + " " + systemConstants.attendanceTime);//上班时间的日期对象
        Date d3 = DateUtil.parse(DateUtil.today() + " " + systemConstants.attendanceEndTime); //签到结束时间
        int status = 1; // 正常考勤
        if (d1.compareTo(d2) <= 0) {
            status = 1; // 早于上班时间正常签到
        } else if (d1.compareTo(d2) > 0 && d1.compareTo(d3) < 0) {
            status = 2; //迟到
        }
        // 查询签到人的人脸模型数据
        int userId = (Integer) param.get("userId");
        String faceModel = faceModelDao.searchFaceModel(userId);
        if (faceModel == null) {
            throw new EmosException("不存在人脸模型");
        } else {
            String path = (String) param.get("path");// 在hashMap中获取图片路径
            HttpRequest request = HttpUtil.createPost(checkinUrl);//向人脸识别程序发出http请求
            request.form("photo", FileUtil.file(path), "targetModel", faceModel);// 提交上传图片数据
            request.form("code", code); // 提交code码,python校验
            HttpResponse response = request.execute(); // 发起请求 返回 response
            if (response.getStatus() != 200) {
                log.error("人脸识别服务异常");
                throw new EmosException("人脸识别服务异常");
            }
            String body = response.body(); // 接受响应返回的内容-> 响应体
            if ("无法识别出人脸".equals(body) || "照片中存在多张人脸".equals(body)) {
                throw new EmosException(body);
            } else if ("False".equals(body)) {
                throw new EmosException("签到无效，非本人签到");
            } else if ("True".equals(body)) {
                //TODO 这里要获取签到地区新冠疫情风险等级
                int risk = 1; // 1.低风险 2.中风险 3.高风险
                String city = (String) param.get("city");
                String district = (String) param.get("district");  // 获取城市 地区 信息
                String address = (String) param.get("address");
                String country = (String) param.get("country");
                String province = (String) param.get("province"); // 在前端传入的Hashmap中提取位置数据
                //二者都不为空
                if (!StrUtil.isBlank(city) && !StrUtil.isBlank(district)) {
                    String code = cityDao.searchCode(city);  //得到城市编号
                    try {
                        String url = "http://m." + code + ".bendibao.com/news/yqdengji/?qu=" + district; //发起请求路径
                        Document document = Jsoup.connect(url).get();//发起http get 请求，返回HTML,Jsoup解析
                        Elements elements = document.getElementsByClass("list-content");// 根据css样式查找控件,存放在集合里
                        if (elements.size() > 0) {
                            Element element = elements.get(0); //取出第一个元素
                            String result = element.select("p:last-child").text();//在div控件中查找最后一个p标签,取出内容
//                            result = "高风险";
                            if ("高风险".equals(result)) {
                                risk = 3;
                                // TODO 发送告警邮件
                                HashMap<String, String> map = userDao.searchNameAndDept(userId); //查询员工的信息
                                String name = map.get("name");
                                String deptName = map.get("dept_name"); // 取出姓名部门
                                deptName = deptName != null ? deptName : ""; // 不为空则不赋值，为空设置为空字符串
                                SimpleMailMessage message = new SimpleMailMessage();//创建邮件的封装对象
                                message.setTo(hrEmail);
                                message.setSubject("员工" + name + "身处高风险疫情地区警告");
                                message.setText(deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，属于新冠疫情高风险地区，请及时与该员工联系，核实情况！");
                                emailTask.sendAsync(message); // 异步，发送邮件
                            } else if ("中风险".equals(result)) {
                                risk = 2;
                            }
                        }
                    } catch (Exception e) {
                        log.error("执行异常", e);
                        throw new EmosException("获取疫情风险等级失败");
                    }
                }
                //保存签到记录
                TbCheckin entity = new TbCheckin();
                entity.setUserId(userId);
                entity.setAddress(address);
                entity.setCountry(country);
                entity.setProvince(province);
                entity.setCity(city);
                entity.setDistrict(district);
                entity.setStatus((byte) status);
                entity.setRisk(risk);
                entity.setDate(DateUtil.today());
                entity.setCreateTime(d1);  // 插入数据
                checkinDao.insert(entity); // 存入数据表
            }
        }
    }

    /**
     * 创建人脸模型
     */
    @Override
    public void createFaceModel(int userId, String path) {
        HttpRequest request = HttpUtil.createPost(createFaceModelUrl); // 创建post请求对象,request接受方法返回的对象
        request.form("photo", FileUtil.file(path));
        request.form("code", code); // 设置需要上传的文件
        HttpResponse response = request.execute(); // 发出请求,返回响应
        String body = response.body(); // 获得响应体->body
        if ("无法识别出人脸".equals(body) || "照片中存在多张人脸".equals(body)) {
            throw new EmosException(body);
        } else {
            TbFaceModel entity = new TbFaceModel();
            entity.setUserId(userId);
            entity.setFaceModel(body);
            faceModelDao.insert(entity);
        }
    }

    /**
     * 查询今日签到结果
     */
    @Override
    public HashMap searchTodayCheckin(int userId) {
        HashMap map = checkinDao.searchTodayCheckin(userId);
        return map;
    }

    /**
     * 统计用户总的签到天数
     */
    @Override
    public long searchCheckinDays(int userId) {
        long days = checkinDao.searchCheckinDays(userId);
        return days;
    }

    /**
     * 查询员工本周签到情况
     */
    @Override
    public ArrayList<HashMap> searchWeekCheckin(HashMap param) {
        ArrayList<HashMap> checkinList = checkinDao.searchWeekCheckin(param); //本周签到情况
        ArrayList<String> holidaysList = holidaysDao.searchHolidaysInRange(param); //本周特殊节假日
        ArrayList<String> workdayList = workdayDao.searchWorkdayInRange(param);//本周特殊工作日

        DateTime startDate = DateUtil.parseDate(param.get("startDate").toString());//获取本周开始日期
        DateTime endDate = DateUtil.parseDate(param.get("endDate").toString());//获取本周结束日期
        DateRange range = DateUtil.range(startDate, endDate, DateField.DAY_OF_MONTH);//通过range()函数生成本周七天连续的日期对象

        ArrayList<HashMap> list = new ArrayList<>();

        range.forEach(one -> {  //forEach()实现for循环
            String date = one.toString("yyyy-MM-dd");//得到日期字符串，规定格式
            //查看今天是不是假期或者工作日
            String type = "工作日";
            if (one.isWeekend()) {
                type = "节假日";
            }
            if (holidaysList != null && holidaysList.contains(date)) {
                type = "节假日";
            } else if (workdayList != null && workdayList.contains(date)) {
                type = "工作日";
            }

            //查看考勤结果
            String status = "";
            //DateUtil.compare(one, DateUtil.date()) <= 0 one表示七天中的某天，date表示当前，one<date说明one这天已经过了
            if (type.equals("工作日") && DateUtil.compare(one, DateUtil.date()) <= 0) { //表示在过去的几天
                status = "缺勤";
                boolean flag = false;
                for (HashMap<String, String> map : checkinList) {
                    if (map.containsValue(date)) { //查看map中是否含有当前日期
                        status = map.get("status");
                        flag = true;
                        break;
                    }
                    //把考勤结束时间封装进 endTime 对象
                    DateTime endTime = DateUtil.parse(DateUtil.today() + " " + systemConstants.attendanceTime);
                    String today = DateUtil.today(); //当前日期字符串
                    //如果one恰好是今天，当前时间小于考勤结束时间
                    if (date.equals(today) && DateUtil.date().isBefore(endTime) && flag == false) {
                        status = "";
                    }
                }
            }

            //封装进map,时间，考勤结果
            HashMap map = new HashMap();
            map.put("date", date);
            map.put("status", status);
            map.put("type", type);
            map.put("day", one.dayOfWeekEnum().toChinese("周"));
            list.add(map);
        });
        return list;
    }

    /**
     * 查询月考勤
     */
    @Override
    public ArrayList<HashMap> searchMonthCheckin(HashMap param) {
        return this.searchWeekCheckin(param); //查询月考勤，使用的方法时查询一周的考勤情况，但是传入的参数的日期是一个月,返回的也是一个月的考勤
    }
}